﻿using MathParser.DataModel;

namespace MathParser {
    public partial class Parser {
        public string Differenziere() {
            // vor dem Differenzieren den Term zusammenfassen
            string termZusammengefasst = JoinForIntegralDifferentialCalculation(false);
            if (this.Expression != termZusammengefasst) {
                var parNew = new Parser();
                parNew.Parse(termZusammengefasst);
                return parNew.Differenziere();
            }
            string term1;
            string innereAbl;
            string ls;
            string rs;
            string ergebnis = "";
            switch (this.OpTyp) {
            case Operation.Cosinus:
            case Operation.Sinus:
            case Operation.Tangens:
            case Operation.Sqrt:
            case Operation.Ln:
                #region sin, cos, tan, sqrt, ln
                term1 = this.ChildLeft.Expression;
                innereAbl = this.ChildLeft.Differenziere();
                //
                string auessereAbleitung1 = "";
                string auessereAbleitung2 = "";
                switch (this.OpTyp) {
                    case Operation.Cosinus:
                        auessereAbleitung1 = "-sin(";
                        auessereAbleitung2 = ")";
                        break;
                    case Operation.Sinus:
                        auessereAbleitung1 = "cos(";
                        auessereAbleitung2 = ")";
                        break;
                    case Operation.Tangens:
                        auessereAbleitung1 = "1+tan(";
                        auessereAbleitung2 = ")^2";
                        break;
                    case Operation.Sqrt:
                        auessereAbleitung1 = "1/2*(";
                        auessereAbleitung2 = ")^(-1/2)";
                        break;
                    case Operation.Ln:
                        auessereAbleitung1 = "1/(";
                        auessereAbleitung2 = ")";
                        break;
                }
                //
                if (innereAbl == "1")
                    ergebnis = auessereAbleitung1 + term1 + auessereAbleitung2;
                else if (innereAbl == "0")
                    ergebnis = "0";
                else
                    ergebnis = auessereAbleitung1 + term1 + auessereAbleitung2 + "*(" + innereAbl + ")";
                break;
                #endregion
            case Operation.Potenz:
                #region potenz
                term1 = this.ChildLeft.Expression;
                string term2 = this.ChildRight.Expression;
                if (term1.IndexOf("x") != -1) {
                    // Variable in der Basis, also Ableitungen von f(x)=x^n
                    double expo = this.ChildRight.Caluculate(0);
                    double expo1 = expo - 1;
                    if ((term1 == "x") && (expo1 > 0)) {
                        ergebnis = expo + "*" + term1 + "^" + expo1;
                    }
                    else {
                        string innereAbleitung = this.ChildLeft.Differenziere();
                        AddBracketsForPlusAndMinusOperations(ref innereAbleitung);
                        ergebnis = expo + "*(" + term1 + ")^(" + expo1 + ")*" + innereAbleitung;
                    }
                }
                else {
                    // Variable im Exponenten
                    if ((term1 == "e") && (term2 == "x")) // f(x)=e^x => f'(x)=e^x
                        {
                        ergebnis = "e^x";
                    }
                    else if ((term1 == "e") && (term2 != "x")) {
                        string innereAbleitung = this.ChildRight.Differenziere();
                        AddBracketsForPlusAndMinusOperations(ref innereAbleitung);
                        AddBracketsForBasicOperations(ref term2);
                        ergebnis = innereAbleitung + "*e^" + term2;
                    }
                    else {
                        double basis = double.Parse(term1);
                        if (basis > 0) {
                            // f(x)=a^x => f'(x)=a^x*ln(a), für a>0
                            ergebnis = term1 + "^" + term2 + "*ln(" + term1 + ")";
                        }
                    }
                }
                #endregion
                break;
            case Operation.Plus:
                #region plus
                term1 = this.ChildLeft.Differenziere();
                innereAbl = this.ChildRight.Differenziere();
                if (term1 == "0")
                    ergebnis = innereAbl;
                else if (innereAbl == "0")
                    ergebnis = term1;
                else
                    ergebnis = term1 + "+" + innereAbl;
                #endregion
                break;
            case Operation.Minus:
                #region minus
                term1 = this.ChildLeft.Differenziere();
                innereAbl = this.ChildRight.Differenziere();
                AddBracketsForPlusAndMinusOperations(ref innereAbl);
                if (term1 == "0")
                    ergebnis = "(-" + innereAbl + ")";
                else if (innereAbl == "0")
                    ergebnis = term1;
                else
                    ergebnis = term1 + "-" + innereAbl;
                #endregion
                break;
            case Operation.Multi:
                #region multi
                term1 = this.ChildLeft.Differenziere();
                innereAbl = this.ChildRight.Expression;
                AddBracketsForPlusAndMinusOperations(ref term1);
                AddBracketsForPlusAndMinusOperations(ref innereAbl);
                if ((term1 != "0") & (innereAbl != "0")) {
                    ls = term1 + "*" + innereAbl;
                }
                else {
                    ls = "0";
                }
                term1 = this.ChildLeft.Expression;
                innereAbl = this.ChildRight.Differenziere();
                AddBracketsForPlusAndMinusOperations(ref term1);
                AddBracketsForPlusAndMinusOperations(ref innereAbl);
                if ((term1 != "0") & (innereAbl != "0")) {
                    rs = term1 + "*" + innereAbl;
                }
                else {
                    rs = "0";
                }
                if (ls == "0") {
                    ergebnis = rs;
                }
                else if (rs == "0") {
                    ergebnis = ls;
                }
                else {
                    ergebnis = ls + "+" + rs;
                }
                break;
                #endregion
            case Operation.Divi:
                #region divi
                term1 = this.ChildLeft.Differenziere();
                innereAbl = this.ChildRight.Expression;
                string zwergebnis;
                AddBracketsForPlusAndMinusOperations(ref term1);
                AddBracketsForPlusAndMinusOperations(ref innereAbl);

                if ((term1 != "0") & (innereAbl != "0")) {
                    ls = term1 + "*" + innereAbl;
                }
                else {
                    ls = "0";
                }
                term1 = this.ChildLeft.Expression;
                AddBracketsForPlusAndMinusOperations(ref term1);
                AddBracketsForPlusAndMinusOperations(ref innereAbl);
                innereAbl = this.ChildRight.Differenziere();

                if ((term1 != "0") & (innereAbl != "0")) {
                    rs = term1 + "*" + innereAbl;
                }
                else {
                    rs = "0";
                }
                if (ls == "0") {
                    zwergebnis = "(-" + rs + ")";
                }
                else if (rs == "0") {
                    zwergebnis = ls;
                }
                else {
                    zwergebnis = ls + "-" + rs;
                }
                term1 = this.ChildRight.Expression;
                AddBracketsForPlusAndMinusOperations(ref zwergebnis);
                AddBracketsForPlusAndMinusOperations(ref term1);
                ergebnis = zwergebnis + "/" + term1 + "^2";
                #endregion
                break;
            case Operation.Variable:
                ergebnis = "1";
                break;
            case Operation.Zahl:
                ergebnis = "0";
                break;
            case Operation.Pi:
            case Operation.E:
                ergebnis = "1";
                break;
            case Operation.Lg:
                break;
            case Operation.Lb:
                break;
            case Operation.Fakultät:
                break;
            }
            // nach dem Differenzieren den Term zusammenfassen
            var par2 = new Parser();
            par2.Parse(ergebnis);
            var endErgebnis = par2.JoinForIntegralDifferentialCalculation(true);
            return endErgebnis;
        }
    }
}